﻿using System;
using System.Linq;
using System.Net.Mime;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;

namespace Lztkdr.ParameterBinder
{
    public class ValueParser
    {
        private ILogger<AutoBinder> _logger;

        public ModelBindingContext BindingContext { get; set; }

        /// <summary>
        /// 请求URL数据
        /// </summary>
        public JObject JsonQueryObject { get; private set; } = new JObject();

        /// <summary>
        /// 请求体头数据
        /// </summary>
        public JObject JsonHeaderObject { get; private set; } = new JObject();

        /// <summary>
        /// 表单提交 或 请求体json
        /// </summary>
        public JObject JsonBodyObject { get; private set; } = new JObject();

        /// <summary>
        /// 请求体json数组
        /// </summary>
        public JArray JsonBodyArray { get; private set; } = new JArray();

        /// <summary>
        /// 请求的Cookie数据
        /// </summary>
        public JObject JsonCookieObject { get; private set; } = new JObject();

        /// <summary>
        /// 请求体原始数据
        /// </summary>
        public string RequestRaw { get; private set; }

        /// <summary>
        /// 请求方法
        /// </summary>
        public string Method { get; set; }

        /// <summary>
        /// 请求数据的类型
        /// </summary>
        public string MediaType { get; set; }

        public ValueParser(ILogger<AutoBinder> logger, ModelBindingContext bindingContext)
        {
            this._logger = logger;
            this.BindingContext = bindingContext;
            this.Init();
        }

        /// <summary>
        /// 解析
        /// </summary>
        private void Init()
        {
            HttpContext context = this.BindingContext.HttpContext;
            this.Method = context.Request.Method.ToUpper();
            var contentType = context.Request.ContentType;
            this.MediaType = string.Empty;
            var charSet = "utf-8";
            Encoding encoding = Encoding.GetEncoding(charSet);
            if (!string.IsNullOrWhiteSpace(contentType))
            {
                var cttType = new ContentType(contentType);
                if (!string.IsNullOrWhiteSpace(cttType.CharSet))
                {
                    charSet = cttType.CharSet;
                }
                this.MediaType = cttType.MediaType.ToLower();
            }

            if (context.Request.Query != null && context.Request.Query.Count > 0)
            {
                JsonQueryObject = new JObject();
                foreach (var item in context.Request.Query)
                {
                    if (item.Value.Count > 1)
                    {
                        var jArr = new JArray();
                        foreach (var val in item.Value)
                        {
                            jArr.Add(val);
                        }
                        JsonQueryObject.Add(item.Key, jArr);
                    }
                    else
                    {
                        JsonQueryObject.Add(item.Key, item.Value.FirstOrDefault());
                    }
                }
            }

            if (context.Request.Headers != null && context.Request.Headers.Count > 0)
            {
                JsonHeaderObject = new JObject();
                foreach (var item in context.Request.Headers)
                {
                    if (item.Value.Count > 1)
                    {
                        var jArr = new JArray();
                        foreach (var val in item.Value)
                        {
                            jArr.Add(val);
                        }
                        JsonHeaderObject.Add(item.Key, jArr);
                    }
                    else
                    {
                        JsonHeaderObject.Add(item.Key, item.Value.FirstOrDefault());
                    }
                }
            }

            if ((MediaType == "application/x-www-form-urlencoded" || MediaType == "multipart/form-data") && context.Request.Form != null && context.Request.Form.Count > 0)
            {
                JsonBodyObject = new JObject();
                foreach (var item in context.Request.Form)
                {
                    if (item.Value.Count > 1)
                    {
                        var jArr = new JArray();
                        foreach (var val in item.Value)
                        {
                            jArr.Add(val);
                        }
                        JsonBodyObject.Add(item.Key, jArr);
                    }
                    else
                    {
                        JsonBodyObject.Add(item.Key, item.Value.FirstOrDefault());
                    }
                }
            }

            if (context.Request.Cookies != null && context.Request.Cookies.Count > 0)
            {
                JsonCookieObject = new JObject();
                foreach (var item in context.Request.Cookies)
                {
                    JsonCookieObject.Add(item.Key, item.Value);
                }
            }

            this.RequestRaw = context.GetBodyString(encoding)?.Trim();

            if (MediaType == "application/json")
            {
                #region json数据提交

                if (RequestRaw.StartsWith("{") && RequestRaw.EndsWith("}"))
                {
                    try
                    {
                        JsonBodyObject = JObject.Parse(RequestRaw);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogWarning(ex, "AutoBinderMiddleware 解析 jsonObject 失败:" + RequestRaw);
                    }
                }
                else if (RequestRaw.StartsWith("[") && RequestRaw.EndsWith("]"))
                {
                    try
                    {
                        JsonBodyArray = JArray.Parse(RequestRaw);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogWarning(ex, "AutoBinderMiddleware 解析 jsonArray 失败:" + RequestRaw);
                    }
                }
                #endregion
            }
        }


        public void ParsingSetValue(ModelBindingContext bindingContext)
        {
            this.BindingContext = bindingContext;
            var attr = bindingContext.GetAttribute<AutoBinderAttribute>();
            var fieldName = attr?.FieldName ?? bindingContext.FieldName;
            var modelType = bindingContext.ModelType;

            if (string.IsNullOrWhiteSpace(fieldName))
            {
                return;
            }

            object targetVal = null;
            try
            {
                //原始请求body
                if (fieldName.Equals("rowBody", StringComparison.OrdinalIgnoreCase) && modelType == typeof(string))
                {
                    targetVal = RequestRaw;
                }
                else if (modelType.IsSimpleType() || modelType.IsSimpleArrayType() || modelType.IsSimpleListType())
                {
                    if (attr.Type == AutoType.None)
                    {
                        #region 未执行绑定类型时，则只会从 Query 或 Body 中绑定参数值
                        if (this.Method == "GET")
                        {
                            if (this.JsonQueryObject.TryGetValue(fieldName, StringComparison.OrdinalIgnoreCase, out var jtoken))
                            {
                                targetVal = jtoken.ConvertObject(modelType);
                            }
                        }
                        else
                        {
                            //fieldName 有可能是 字段名，也有可能是 jsonPath
                            var selToken = this.JsonBodyObject.SelectToken(fieldName);
                            if (selToken != null)
                            {
                                targetVal = selToken.ConvertObject(modelType);
                            }

                            //接收参数是 数组或集合
                            if (selToken == null && (modelType.IsSimpleArrayType() || modelType.IsSimpleListType()) && this.JsonBodyArray.Count > 0)
                            {
                                targetVal = this.JsonBodyArray.ConvertObject(modelType);
                            }
                        } 
                        #endregion
                    }
                    else if (attr.Type == AutoType.Query)
                    {
                        if (this.JsonQueryObject.TryGetValue(fieldName, StringComparison.OrdinalIgnoreCase, out var jtoken))
                        {
                            targetVal = jtoken.ConvertObject(modelType);
                        }
                    }
                    else if (attr.Type == AutoType.Form || attr.Type == AutoType.Body)
                    {
                        if (fieldName.Contains(".") || (fieldName.Contains("[") && fieldName.Contains("]")))
                        {
                            var selToken = this.JsonBodyObject.SelectToken(fieldName);
                            if (selToken != null)
                            {
                                targetVal = selToken.ConvertObject(modelType);
                            }
                        }
                        else if ((modelType.IsSimpleArrayType() || modelType.IsSimpleListType()) && this.JsonBodyArray.Count > 0)
                        {
                            targetVal = this.JsonBodyArray.ConvertObject(modelType);
                        }
                        else
                        {
                            if (this.JsonBodyObject.TryGetValue(fieldName, StringComparison.OrdinalIgnoreCase, out var jtoken))
                            {
                                targetVal = jtoken.ConvertObject(modelType);
                            }
                        }
                    }
                    else if (attr.Type == AutoType.Header)
                    {
                        if (this.BindingContext.ActionContext.HttpContext.Request.Headers.TryGetValue(fieldName, out var strVals))
                        {
                            targetVal = strVals.ConvertObject(modelType);
                        }
                    }
                    else if (attr.Type == AutoType.Cookie)
                    {
                        if (this.BindingContext.ActionContext.HttpContext.Request.Cookies.TryGetValue(fieldName, out var strVals))
                        {
                            targetVal = strVals.ConvertObject(modelType);
                        }
                    }
                    else if (attr.Type == AutoType.Route)
                    {
                        if (this.BindingContext.ActionContext.RouteData.Values.TryGetValue(fieldName, out var strVals))
                        {
                            targetVal = strVals.ConvertObject(modelType);
                        }
                    }
                }
                else // 复杂对象 或 IList<复杂对象>  或 IDictionary
                {
                    if (attr.Type == AutoType.None)
                    {
                        if (this.Method == "GET")
                        {
                            //限制 只有 1个参数的复合参数对象，才进行映射
                            if (bindingContext.ActionContext.ActionDescriptor.Parameters.Count == 1)
                            {
                                targetVal = this.JsonQueryObject.ConvertObject(modelType);
                            }
                        }
                        else
                        {
                            if (fieldName.Contains(".") || (fieldName.Contains("[") && fieldName.Contains("]")))
                            {
                                var selToken = this.JsonBodyObject.SelectToken(fieldName);
                                if (selToken != null)
                                {
                                    targetVal = selToken.ConvertObject(modelType);
                                }
                            }
                            else if ((modelType.IsSimpleArrayType() || modelType.IsSimpleListType()) && this.JsonBodyArray.Count > 0)
                            {
                                targetVal = this.JsonBodyArray.ConvertObject(modelType);
                            }
                            else
                            {
                                targetVal = this.JsonBodyObject.ConvertObject(modelType);
                            }
                        }
                    }
                    else if (attr.Type == AutoType.Query)
                    {
                        targetVal = this.JsonQueryObject.ConvertObject(modelType);
                    }
                    else if (attr.Type == AutoType.Form || attr.Type == AutoType.Body)
                    {
                        if (fieldName.Contains(".") || (fieldName.Contains("[") && fieldName.Contains("]")))
                        {
                            var selToken = this.JsonBodyObject.SelectToken(fieldName);
                            if (selToken != null)
                            {
                                targetVal = selToken.ConvertObject(modelType);
                            }
                        }
                        else if ((modelType.IsSimpleArrayType() || modelType.IsSimpleListType()) && this.JsonBodyArray.Count > 0)
                        {
                            targetVal = this.JsonBodyArray.ConvertObject(modelType);
                        }
                        else
                        {
                            targetVal = this.JsonBodyObject.ConvertObject(modelType);
                        }
                    }
                    else if (attr.Type == AutoType.Header)
                    {
                        targetVal = this.JsonHeaderObject.ConvertObject(modelType);
                    }
                    else if (attr.Type == AutoType.Cookie)
                    {
                        targetVal = this.JsonCookieObject.ConvertObject(modelType);
                    }
                    else if (attr.Type == AutoType.Route)
                    {
                        targetVal = this.BindingContext.ActionContext.RouteData.Values.ConvertObject(modelType);
                    }
                }

                if (targetVal != null)
                {
                    this.BindingContext.Result = ModelBindingResult.Success(targetVal);
                }
                else
                {
                    if (modelType.IsValueType && targetVal == null)
                    {
                        this.BindingContext.Result = ModelBindingResult.Failed();
                    }
                    else
                    {
                        this.BindingContext.Result = ModelBindingResult.Failed();
                    }
                }
            }
            catch (Exception ex)
            {
                this._logger.LogWarning(ex, "ModelBindingResult.Failed!");
                this.BindingContext.Result = ModelBindingResult.Failed();
            }
           
        }
    }
}
